#region Using References

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

#endregion

namespace Beaker.Specs
{
	/// <summary>
	/// Creates random values
	/// </summary>
	public sealed class Some
	{
		private static readonly Random Random = new Random();

		/// <summary>
		/// Creates some random date between 1980 and 2030
		/// </summary>
		public DateTime Date()
		{
			int days = Random.Next( 365 * 50 );
			return new DateTime( 1980, 1, 1 ).AddDays( days );
		}

		/// <summary>
		/// Creates some random date and time between 1980 and 2030
		/// </summary>
		public DateTime DateTime()
		{
			int seconds = Random.Next( 365 * 50 * 24 * 60 * 60 );
			return new DateTime( 1980, 1, 1 ).AddSeconds( seconds );
		}

		/// <summary>
		/// Creates some random time (00:00:00 - 23:59:59)
		/// </summary>
		/// <returns></returns>
		public TimeSpan Time()
		{
			int seconds = Random.Next( 24 * 60 * 60 );
			return new TimeSpan( seconds * 1000 );
		}

		/// <summary>
		/// Creates some random byte
		/// </summary>
		/// <returns>A random byte</returns>
		public Byte Byte()
		{
			var buffer = new byte[1];
			Random.NextBytes( buffer );
			return buffer[0];
		}

		/// <summary>
		/// Creates some random short number
		/// </summary>
		/// <returns>A random number</returns>
		public Int16 Int16()
		{
			var buffer = new byte[2];
			Random.NextBytes( buffer );
			return BitConverter.ToInt16( buffer, 0 );
		}


		/// <summary>
		/// Creates some random int number
		/// </summary>
		/// <returns>A random number</returns>
		public Int32 Int32()
		{
			var buffer = new byte[4];
			Random.NextBytes( buffer );
			return BitConverter.ToInt32( buffer, 0 );
		}

		/// <summary>
		/// Creates some random int number
		/// </summary>
		/// <returns>A random number</returns>
		public Int32 Int32( int minValue, int maxValue )
		{
			return Random.Next( minValue, maxValue );
		}

		/// <summary>
		/// Creates some random long number
		/// </summary>
		/// <returns>A random number</returns>
		public Int64 Int64()
		{
			var buffer = new byte[8];
			Random.NextBytes( buffer );
			return BitConverter.ToInt64( buffer, 0 );
		}


		/// <summary>
		/// Creates some random long within range
		/// </summary>
		/// <param name="minValue">The min value.</param>
		/// <param name="maxValue">The max value.</param>
		/// <returns></returns>
		public Int64 Int64( long minValue, long maxValue = long.MaxValue )
		{
			var buf = new byte[8];
			Random.NextBytes( buf );
			long longRand = BitConverter.ToInt64( buf, 0 );

			return ( Math.Abs( longRand % ( maxValue - minValue ) ) + minValue );
		}

		/// <summary>
		/// Creates randon enum value for type T and skip enum values in skip[]
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public T Enum<T>( params T[] skip )
		{
			if( !typeof( T ).IsEnum )
				throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, "{0} is not a Enum.", typeof( T ).FullName ) );

			IEnumerable<T> enumValues = System.Enum.GetValues( typeof( T ) ).OfType<T>().Except( skip );

			if( !enumValues.Any() )
				throw new Exception( "No enum values to return." );

			int randomIndex = Int32( 0, enumValues.Count() - 1 );
			return enumValues.ElementAt( randomIndex );
		}

		/// <summary>
		/// Creates some random string between <see cref="minChars"/> and <see cref="maxChars"/> in length
		/// </summary>
		/// <param name="minChars">The min chars of the string</param>
		/// <param name="maxChars">The max chars of the string</param>
		/// <returns>A random string between <see cref="minChars"/> and <see cref="maxChars"/> characters in length</returns>
		public string String( int minChars = 8, int maxChars = 32 )
		{
			int chars = Random.Next( minChars, maxChars );

			var buffer = new byte[chars * 2];
			for( int i = 0; i < chars; i++ )
			{
				int chr = Random.Next( 0xd7ff );
				buffer[i + 1] = (byte)( ( chr & 0xff00 ) >> 8 );
				buffer[i] = (byte)( chr & 0xff );
			}

			return Encoding.Unicode.GetString( buffer );
		}
	}
}